package ogmios.dialogs;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import javax.swing.text.*;
import javax.swing.event.*;
import java.util.regex.*;
import java.util.*;
import java.io.*;
import ogmios.core.*;
import ogmios.dialogs.*;
import ogmios.processes.*;
import ogmios.components.*;
import ogmios.components.munches.*;
import static ogmios.components.munches.Munch.MunchSetting;
/**
 * An mini-editor designed for editting and compiling Munches.
 * <br>
 * This may be eventually merged into Ogmios' editor.
 */
public class Munchkin extends JDialog
{
  public JTabbedPane tabs;
  public FindAndReplaceDialog findAndReplaceDialog;
  public Map<String, MunchTab> munchTabs = new HashMap<String, MunchTab>();
  public static final String FIND_AND_REPLACE_MUNCH_ACTION = "FindAndReplace";
  public final Action FIND_AND_REPLACE_MUNCH_ACTION_LISTENER = 
   new AbstractAction()
   {
    public void actionPerformed(ActionEvent e)
    {
      JTextComponent text = (JTextComponent)e.getSource();
      findAndReplaceDialog.showDialog(SwingUtilities.getWindowAncestor(text));
    }
   };

 public static void main(String[] args) throws Throwable
 {
  System.out.println(Munch.class.isAssignableFrom(ExtensionMunch.class));
 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  Ogmios.ogmiosGUI.jAddress = "/usr/lib/jvm/java-6-sun-1.6.0.22/bin";
  Munchkin mk = new Munchkin();
  mk.registerMunch(new ExtensionMunch());
  mk.registerMunch(new CommandMunch());
  mk.setVisible(true);
 }
 public Munchkin()
 {
   this(null);
 }
 public Munchkin(Frame owner)
 {
  super(owner);
  Toolkit toolkit = Toolkit.getDefaultToolkit();
  setTitle("Munchkin"); 
  setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  setModal(true);
  tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
  findAndReplaceDialog = new FindAndReplaceDialog();
  Dimension screenSize = toolkit.getScreenSize();
  screenSize.width /= 3;
  screenSize.height /= 1.5;
  getContentPane().add(tabs); 
  setSize(screenSize);
  addWindowListener(
   new WindowAdapter()
   {
    public void windowClosing(WindowEvent e)
    {
     {
     MultiSaveDialog savePrompt = new MultiSaveDialog();
     for (String munchTabName : munchTabs.keySet())
     {
       MunchTab munchTab = munchTabs.get(munchTabName);
       munchTab.stop.doClick();
       if (munchTab.code.isEditable())
       {
        savePrompt.addSavable(munchTab);
       }
     }
     if (!savePrompt.canClose(Munchkin.this))
     {
       return;
      }
     }
     e.getWindow().setVisible(false);
    }
   }
   );
 }
 /**
 * Launches Munchkin and returns a MunchCapsule about the launched Munch
 * 
 * @return    A MunchCapsule containing a launched plugin, or null
 */
 public MunchCapsule build()
 {
  return build(null);
 }

 /**
 * Launches Munchkin and returns a MunchCapsule about the launched Munch
 * 
 * @param    host    The component to open Munchkin relative to
 * @return    A MunchCapsule containing a launched plugin, or null
 */
 public MunchCapsule build(Component host)
 {
  return build(null, null);
 }
 
 /**
 * Launches Munchkin and returns a MunchCapsule about the launched Munch
 * 
 * @param    host    The component to open Munchkin relative to
 * @param    munchName     The name of the MunchTab to open
 * @return    A MunchCapsule containing a launched plugin, or null
 */
 public MunchCapsule build(Component host, String munchName)
 {
  setLocationRelativeTo(host);
  MunchTab munchTab = munchTabs.get(munchName);
  if (munchTab != null)
  {
   tabs.setSelectedComponent(munchTab);
  }
   setVisible(true);
   if (tabs.getTabCount() > 0)
   {
     munchTab = (MunchTab)tabs.getSelectedComponent();
     if (munchTab.launching)
     {
      munchTab.launching=false;
      return new MunchCapsule(munchTab.currentFileName,munchTab.munch);
     }
   }
   return null;
 }

 /**
 * Adds the Munch to Munchkin, as long as no other MunchTab with the same name (specifically, the name returned by munch.getName()) is already registered.
 */
 public void registerMunch(Munch munch)
 {
    registerMunchTab(new MunchTab(munch));
 }

 /**
 * Adds the MunchTab to Munchkin, as long as no other MunchTab with the same name (specifically, the name returned by munchTab.getName()) is already registered.
 */
 public void registerMunchTab(MunchTab munchTab)
 {
   if (munchTabs.get(munchTab.getName())==null)
    addMunchTab(munchTab);
 }

 /**
 * Adds the MunchTab to Munchkin
 */
 private void addMunchTab(MunchTab munchTab)
 {
  munchTabs.put(munchTab.getName(), munchTab);
  addFindAndReplace(munchTab);
  tabs.add(munchTab);
 }

 /**
 * Adds a Find And Replace Dialog to the MunchTab, using the FIND_AND_REPLACE_MUNCH_ACTION as the key
 * for FIND_AND_REPLACE_MUNCH_ACTION_LISTENER added to the MunchTab's code.
 */
 public void addFindAndReplace(MunchTab munchTab)
 {
  JTextComponent text = munchTab.code;
  text.getInputMap(JComponent.WHEN_FOCUSED).put(
   KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
   FIND_AND_REPLACE_MUNCH_ACTION);
  text.getActionMap().put(FIND_AND_REPLACE_MUNCH_ACTION, FIND_AND_REPLACE_MUNCH_ACTION_LISTENER);
 }

 /**
 * A tab containing all the methods and GUI controls necessary to edit code for the underlying Munch.
 */
 public static class MunchTab extends JPanel implements Savable
 {
  public static String DEFAULT_ERROR_REGEX = ":(\\d+)";
  public static int MAX_UNDO_LEVEL = 1024;
  /**
  * The default action listener for creating a new file for this munch
  */
  public static final ActionListener NEW_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.canChange())
     {
       String munchName = munchTab.munch.getName();
       String fileName = JOptionPane.showInputDialog(null, "Name of new " + munchName, "New " + munchName, JOptionPane.QUESTION_MESSAGE);
       if (fileName != null && (fileName = fileName.trim()).length() > 0)
       {
         File newFile = new File(munchTab.munch.getFolder() + File.separator + "code" + File.separator + fileName + ".java");
         if (newFile.exists() && newFile.isFile())
        {
          if (JOptionPane.showConfirmDialog(null, "There is already a file saved with the name " + fileName + 
          "\nDo you want to continue, possibly overwriting?", "Overwrite", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE)
          ==JOptionPane.NO_OPTION)
         {
          return;
         }
        }
        munchTab.newCode(fileName, newFile, false);
       }
     }
   }
  };

  /**
  * The default action listener for opening a file for this munch
  */
  public static final ActionListener OPEN_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.canChange())
     {
       munchTab.loadMunch();
     }     
   }
  };

  /**
  * The default action listener for saving the current file for this munch
  */
  public static final ActionListener SAVE_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.code.isEditable())
      munchTab.save();
   }
  };


  /**
  * The default action listener for renaming the current file for this munch
  */
  public static final ActionListener RENAME_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.code.isEditable())
     {
      String oldName = munchTab.currentFileName;
      String newName = JOptionPane.showInputDialog(munchTab, "Enter a new name", "Rename", JOptionPane.QUESTION_MESSAGE);
      if (newName != null && (newName = newName.trim()).length() > 0)
      {
        if (!new File(munchTab.munch.getFolder() + File.separator + "code" + File.separator + newName + ".java").exists() ||
          JOptionPane.showConfirmDialog(null, "There is already a file saved with the name " + newName + 
          "\nDo you want to continue, possibly overwriting?", "Overwrite", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE)
          ==JOptionPane.YES_OPTION)
        {
         munchTab.currentFileName = newName;
         munchTab.currentFile.setText("*" + newName);
         if (!munchTab.saveNeeded)
         {
          munchTab.code.getDocument().removeDocumentListener(munchTab.CHANGE_LISTENER);
          munchTab.saveNeeded=true;
         }
        }
       }
     }
   }
  };


  /**
  * The default action listener for launching the current file for this munch
  */
  public static final ActionListener LAUNCH_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.code.isEditable())
     {
      munchTab.launching = true;
      SwingUtilities.getWindowAncestor(munchTab).setVisible(false);
     }     
   }
  };


  /**
  * The default action listener for closing the current file for this munch
  */
  public static final ActionListener CLOSE_MUNCH_FILE = 
   new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     if (munchTab.canChange())
     {
       munchTab.vacate();
     }     
   }
  };

  /**
  * The default action listener for dealing with a MunchSetting.STRING setting.
  */
  public static final ActionListener STRING_HANDLER = 
   new ActionListener()
   {
    public void actionPerformed(ActionEvent e)
    {
      Component menuItem = (Component)e.getSource();
      MunchTab munchTab = getMunchTabParent(menuItem);
      String name = menuItem.getName();
      String value = (String)munchTab.settings.get(name); 
      String s = JOptionPane.showInputDialog("Enter in a new value for:\n" + name, value);
      if (s!=null)
      {
       value = s;
       munchTab.settings.put(name, value);
      } 
    }
   };

  /**
  * The default action listener for dealing with a MunchSetting.ARRAY setting.
  */
  public static final ActionListener ARRAY_HANDLER = 
   new ActionListener()
   {
    public void actionPerformed(ActionEvent e)
    {
      Component menuItem = (Component)e.getSource();
      MunchTab munchTab = getMunchTabParent(menuItem);
      String name = menuItem.getName();
      String[] value = (String[])munchTab.settings.get(name); 
      String[] arr;
      if ((arr = ListDialog.showDialog(value, name, "Enter values for: " + name)) != ListDialog.emptyStringArray)
      {
        value = arr;
        munchTab.settings.put(name, arr);
      }   
    }
   };

  /**
  * The default item listener for dealing with a MunchSetting.BOOLEAN setting.
  */
  public static final ItemListener BOOLEAN_HANDLER = 
   new ItemListener()
   {
    public void itemStateChanged(ItemEvent e)
    {
     JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem)e.getSource();
     MunchTab munchTab = getMunchTabParent(menuItem);
     String name = menuItem.getName();
     munchTab.settings.put(name, menuItem.getState());     
    }
   };

 public static final String STOP_ACTION = "Stop";
  /**
  * The default action listener for stopping compilation of the current file.
  */
 public final Action STOP_ACTION_LISTENER = 
  new AbstractAction()
  {
   public void actionPerformed(ActionEvent e)
   {
     if (stop.isVisible() && compiler != null)
     {
      compiler.destroy();
     }
   }
  };
  /**
  * The default action listener for jumping to a given line in the file.
  */
 public static final ActionListener GOTO_LINE = 
  new ActionListener()
  {
   public void actionPerformed(ActionEvent e)
   {
     MunchTab munchTab = getMunchTabParent((Component)e.getSource());
     String s = JOptionPane.showInputDialog(munchTab, "Enter in a line number");
     if (s != null)
      munchTab.selectLine(s.trim());
   }
  };

  /**
  * The default mouse listener for jumping to a line in the file from a JList item based on the default regex.
  */
 public static final MouseListener LIST_GOTO_LINE = 
  new MouseAdapter()
  {
    public void mouseClicked(MouseEvent e)
    {
      JList list = (JList)e.getSource();
      if (list.getModel().getSize() > 0 && e.getClickCount() == 2)
      ((MunchTab)list.getParent().getParent().getParent().getParent()).selectLineFrom(list.getSelectedValue().toString());
    }
  };

 public static final String KEY_GOTO_LINE_ACTION = "GotoLine";
  /**
  * The default key listener for jumping to a line in the file from a JList item based on the default regex.
  */
 public static final Action KEY_GOTO_LINE_ACTION_LISTENER = 
  new AbstractAction()
  {
   public void actionPerformed(ActionEvent e)
   {
     JList list = (JList)e.getSource();
     if (list.getModel().getSize() > 0)
     {
     ((MunchTab)list.getParent().getParent().getParent().getParent()).selectLineFrom(list.getSelectedValue().toString());       
      }
    }
  };
 

 public static final String COMPILE_ACTION = "Compile";
  /**
  * The default action for compiling the current file of this Munch.
  */
 public final Action COMPILE_ACTION_LISTENER = 
  new AbstractAction()
  {
   public void actionPerformed(ActionEvent e)
   {
    if (code.isEditable() && compiler == null)
    {
     save();
     stop.setVisible(true);
     compile.setEnabled(false);
     errLogList.removeAllElements();
     try
     {
       compiler = munch.compile(currentFileName, settings);
       new DefaultProcessWaiter(compiler, cleaner).start();
       new DefaultLineInputThread(errLogList, compiler.getErrorStream()).start();
     }catch(IOException ex)
     {
       compiler = null;
       compile.setEnabled(true);
       stop.setVisible(false);
       OgmiosUtilities.showErrorBox(MunchTab.this, ex, "Process:Compile Error");
     }
    }
   }
  };
  /**
  * The default document listener that alerts if the current document is edited and needs to be saved.
  */
  public final DocumentListener CHANGE_LISTENER = 
   new DocumentListener()
   {
     public void changedUpdate(DocumentEvent e)
      {
      }
      public void insertUpdate(DocumentEvent e)
      {
        saveNeeded(e);
      }
      public void removeUpdate(DocumentEvent e)
      {
       saveNeeded(e);
      }
     public void saveNeeded(DocumentEvent e)
     {
       saveNeeded = true;
       code.getDocument().removeDocumentListener(this);
       currentFile.setText("*" + currentFileName);
     }
   };

  /**
  * The default cleaner to be run after compilation.
  */
 public final Runnable cleaner =
  new Runnable()
  {
   public void run()
   {
    compiler = null;
    compile.setEnabled(true);
    stop.setVisible(false);     
   }
  };

  public boolean launching = false;
  public Process compiler;
  public final Pattern pattern;
  public String currentFileName;
  public boolean saveNeeded = false;
  public final OgmiosTextPane code;
  public final JButton stop;
  public final JButton compile;
  public final JList errLog;
  public final DefaultListModel errLogList;
  public final JSplitPane splitter;
  public final JLabel currentFile;
  public final Munch munch;
  public final Map<String, Object> settings;  
  public MunchTab(Munch munch)
  {
   super(new BorderLayout());
   this.munch = munch;
   pattern = Pattern.compile(DEFAULT_ERROR_REGEX);
   String name;
   setName(name=munch.getName());
   int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
   JMenuBar menuBar = new JMenuBar();
   char firstLetter;
   {
   JMenu fileMenu = new JMenu(name);
    fileMenu.setMnemonic(Character.toUpperCase(firstLetter = name.charAt(0)));
    JMenuItem newMunch = new JMenuItem("New " + name);
     newMunch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, mask));
     newMunch.addActionListener(NEW_MUNCH_FILE);
    fileMenu.add(newMunch);
    JMenuItem openMunch = new JMenuItem("Open " + name);
     openMunch.addActionListener(OPEN_MUNCH_FILE);
     openMunch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, mask));
    fileMenu.add(openMunch);
    JMenuItem saveMunch = new JMenuItem("Save " + name);
     saveMunch.addActionListener(SAVE_MUNCH_FILE);
     saveMunch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, mask));
    fileMenu.add(saveMunch);
    JMenuItem renameMunch = new JMenuItem("Rename " + name);
     renameMunch.addActionListener(RENAME_MUNCH_FILE);
     renameMunch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, mask));
    fileMenu.add(renameMunch);
    JMenuItem launchMunch = new JMenuItem("Launch " + name);
     launchMunch.addActionListener(LAUNCH_MUNCH_FILE);
    fileMenu.add(launchMunch);
    JMenuItem closeMunch = new JMenuItem("Close " + name);
     closeMunch.addActionListener(CLOSE_MUNCH_FILE);
    fileMenu.add(closeMunch);
    menuBar.add(fileMenu);
   }
    Map<String, MunchSetting> settings = munch.getSettings();
    this.settings = new HashMap<String, Object>();
    if (settings != null)
    {
      if (settings.size() != 0)
      {  
        JMenu settingsMenu = new JMenu("Settings");
        settingsMenu.setMnemonic(firstLetter == 'S' ? 'E' : 'S');
        for (String settingName : settings.keySet())
        {
          settingsMenu.add(createSettingHandler(settingName, settings.get(settingName), this.settings));
        }
        menuBar.add(settingsMenu);
      }
    }
   {
    JMenu toolsMenu = new JMenu("Tools");
    toolsMenu.setMnemonic(firstLetter == 'T' ? 'O' : 'T');
    JMenuItem gotoLine = new JMenuItem("Goto Line");
    gotoLine.addActionListener(GOTO_LINE);
    toolsMenu.add(gotoLine);
    menuBar.add(toolsMenu);
   }
   stop = new JButton("Stop");
   stop.setForeground(Color.RED);
   Dimension size = menuBar.getPreferredSize();
   size.height = stop.getPreferredSize().height;
   menuBar.setPreferredSize(size);
   menuBar.add(stop);
   stop.setVisible(false);
   add(menuBar, BorderLayout.NORTH);
   code = new OgmiosTextPane();
   code.setEditable(false);
   code.setName(name + " Code");
   code.getDocument().putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));
   errLog = new JList(errLogList = new DefaultListModel());
   currentFile = new JLabel("Unoccupied", JLabel.CENTER);
   compile = new JButton("Compile");
   JPanel northPanel = new JPanel(new BorderLayout());
   northPanel.add(menuBar, BorderLayout.NORTH);
   northPanel.add(currentFile, BorderLayout.SOUTH);
   add(northPanel, BorderLayout.NORTH);
   splitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT, new JScrollPane(OgmiosUtilities.undoable(code)), new JScrollPane(errLog));
   add(splitter);
   add(compile, BorderLayout.SOUTH);
   splitter.setResizeWeight(0.5);
   splitter.setDividerLocation(0.5);

   stop.addActionListener(STOP_ACTION_LISTENER);
   stop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), STOP_ACTION);
   stop.getActionMap().put(STOP_ACTION, STOP_ACTION_LISTENER);

   compile.addActionListener(COMPILE_ACTION_LISTENER);
   compile.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0), COMPILE_ACTION);
   compile.getActionMap().put(COMPILE_ACTION, COMPILE_ACTION_LISTENER);
 
   errLog.addMouseListener(LIST_GOTO_LINE);
   errLog.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), KEY_GOTO_LINE_ACTION);
   errLog.getActionMap().put(KEY_GOTO_LINE_ACTION, KEY_GOTO_LINE_ACTION_LISTENER);

   File munchFolder = new File(munch.getFolder() + File.separator + "code");
   if (!munchFolder.exists() || !munchFolder.isDirectory())
   {
     munchFolder.mkdirs();
   }
  }
  /**
  * Creates the default component to handle the given munch setting
  *
  * @param    settingName     The name of the setting
  * @param    munchSetting    The MunchSetting type
  * @param    settings     The settings map to insert the default value into (for example, with settingName = 'foo', munchSetting = MunchSetting.STRING, settings.get('foo') equals "" after this method call)
  */
  public static Component createSettingHandler(String settingName, MunchSetting munchSetting, Map<String, Object> settings)
  {
    switch(munchSetting)
    {
      case STRING:
      {
       JMenuItem stringHandler = new JMenuItem(settingName);
       stringHandler.setName(settingName);
       stringHandler.addActionListener(STRING_HANDLER);
       settings.put(settingName,"");
       return stringHandler;
       }
       case ARRAY:
       {
        JMenuItem listHandler = new JMenuItem(settingName);
        listHandler.setName(settingName);
        listHandler.addActionListener(ARRAY_HANDLER);
        settings.put(settingName, ListDialog.emptyStringArray);
        return listHandler;
       }
       default:
       {
        JCheckBoxMenuItem booleanHandler = new JCheckBoxMenuItem(settingName, false);
        booleanHandler.setName(settingName);
        booleanHandler.addItemListener(BOOLEAN_HANDLER);
        settings.put(settingName, false);
        return booleanHandler;
       }
     }
  }
 
  /**
  * Clears Munchkin
  */
  public void vacate()
  {
   if (code.isEditable())
   {
    code.setEditable(false);
    code.setText("");
    currentFileName = "";
    currentFile.setText("Unoccupied");
    clearUndo();
   }
  }


  /**
  * Clears all undos.
  */
  public void clearUndo()
  {
   Document document = code.getDocument();
   UndoManager undo = ((UndoManager)document.getProperty(OgmiosUtilities.UNDOABLE_EDIT_LISTENER));
   undo.setLimit(100);
   undo.discardAllEdits();
   undo.setLimit(MAX_UNDO_LEVEL);
  }

  /**
  * Opens a modified template based on the passed file
  * @param     fileName     The name of the file
  * @param     newFile     The file object representing this fileName
  * @param     alreadySaved     Whether this file is already saved or should be set to unsaved initially
  */
 public void newCode(String fileName, File newFile, boolean alreadySaved)
 {
  setTo(fileName, TemplateManager.modifiedTemplate(munch.getTemplate(), newFile, munch.getTemplateTags()), alreadySaved); 
}

 /**
 * Opens the given file in Munchkin
 * @param     fileName    The name of the file
 * @param     oldFile     The file object to open
 * @param     alreadySaved     Whether this file is already saved or should be set to unsaved initially 
 */
 public void setTo(String fileName, File oldFile, boolean alreadySaved) throws IOException, BadLocationException
 {
  code.setText("");
  FileInputStream stream = new FileInputStream(oldFile);
  code.getEditorKit().read(stream, code.getDocument(), 0);
  Document doc;
  (doc=code.getDocument()).putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));
  stream.close();
  code.setEditable(true);
  code.setCaretPosition(0);
  currentFileName = fileName;
  if (alreadySaved)
  {
   currentFile.setText(fileName);
   saveNeeded=false;
   doc.addDocumentListener(CHANGE_LISTENER);
  }
  else
  {
    currentFile.setText("*" + fileName);
    saveNeeded = true;
  }
  clearUndo();
   
 }

 /**
 * Opens the given text in Munchkin
 * @param     fileName    The name of the file
 * @param     content     The code to open in Munchkin 
 * @param     alreadySaved     Whether this file is already saved or should be set to unsaved initially 
 */
 public void setTo(String fileName, String content, boolean alreadySaved)
 {
  code.setEditable(true);
  code.setText(content);
  Document doc;
  (doc=code.getDocument()).putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));
  code.setCaretPosition(0);
  currentFileName = fileName;
  if (alreadySaved)
  {
   currentFile.setText(fileName);
   saveNeeded=false;
   doc.addDocumentListener(CHANGE_LISTENER);
  }
  else
  {
    currentFile.setText("*" + fileName);
    saveNeeded = true;
  }
  clearUndo();
  
 } 

  public boolean saveNeeded()
  {
    return saveNeeded;
  }

  public void save()
  { 
    try
    {
      File saveFile = new File(munch.getFolder() + File.separator + "code" + File.separator + currentFileName + ".java");
      FileWriter writer = new FileWriter(saveFile);
      Document doc = code.getDocument();
      code.getEditorKit().write(writer, doc, 0, doc.getLength());
      writer.close();     
      if (saveNeeded)
      {
       saveNeeded = false;
       doc.addDocumentListener(CHANGE_LISTENER);
      }
      currentFile.setText(currentFileName);
    }
    catch (Throwable ex)
    {
      OgmiosUtilities.showErrorBox(this, ex, "IO:Save Error");
    }
  }
 
  /**
  * Returns if the current MunchTab can open a different file, or if the current file change is cancelled.
  */
  public boolean canChange()
  {
   if (code.isEditable())
   {
    if (saveNeeded)
    {
 	     switch(JOptionPane.showConfirmDialog(MunchTab.this, "Do you want to save your unsaved work?", "Save",
      JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE))
      {
        case JOptionPane.YES_OPTION:
         save();
        case JOptionPane.NO_OPTION:
         return true;
      }
     return false;
    }
   }
   return true;
  }

 /**
 * Opens a dialog for the user to select an existing Munch
 */
 public void loadMunch()
 {
   DefaultListModel munchList = new DefaultListModel();

   for (File f : new File(munch.getFolder() + File.separator + "code").listFiles(new OgmiosFileFilter(".java", "java")))
     if (f.isFile() && f.canRead())
       munchList.addElement(f.getName().substring(0, f.getName().lastIndexOf(".")));
    if (munchList.size() == 0)
      return;
    final JDialog munchLoader = new JDialog();
    munchLoader.setModal(true);
    munchLoader.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    final JList list  = new JList(munchList);
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    list.addKeyListener(
     new KeyAdapter()
     {
       public void keyPressed(KeyEvent e)
       {
         if (list.getSelectedValue() != null && e.getKeyCode() == KeyEvent.VK_ENTER)
          ((JButton)munchLoader.getContentPane().getComponent(2)).doClick();
        }
       }
      );
    munchLoader.setTitle(munch.getName() + " Loader");

    Container contents = munchLoader.getContentPane();
     JButton load = new JButton("Load");
     load.addActionListener(
       new ActionListener()
      {
       public void actionPerformed(ActionEvent e)
       {
         String s = (String) list.getSelectedValue();
          if (s != null)
          {
            try
            {
             setTo(s, new File(munch.getFolder() + File.separator + "code" + File.separator + s + ".java"), true);
            }
            catch(Throwable ex)
            {
              OgmiosUtilities.showErrorBox(munchLoader, ex, "IO:Read Error");
            }
            munchLoader.setVisible(false);
          }
         }
       }
      );
     contents.add(new JLabel("Looking inside extensions" + File.separator + "code...", JLabel.CENTER), BorderLayout.NORTH);
     contents.add(new JScrollPane(list));
     contents.add(load, BorderLayout.SOUTH);
     munchLoader.pack();
    munchLoader.setLocationRelativeTo(this);
    munchLoader.setVisible(true);
    code.grabFocus();
 }
 
 /**
 * Selects a line in the code based on the line number found in the argument using the default regex
 */
 public void selectLineFrom(String line)
 {
  Matcher matcher = pattern.matcher(line);
  if (matcher.find())
  {
   selectLine(matcher.group(1)); 
  }
 }

 /**
 * Selects a line in the code
 */
 public void selectLine(String lineNumber)
 {
  int line = OgmiosUtilities.tryParse(lineNumber);
  if (OgmiosUtilities.success)
  {
   OgmiosUtilities.gotoLine(code, line);
  }
 }

 public String toString()
 {
  return munch.getName() + ": " + currentFileName;
 }
  /**
  * Returns the MunchTab this JMenuItem belongs to
  */
  protected static MunchTab getMunchTabParent(Component menuItem)
  {
   Component parent = (((JPopupMenu)menuItem.getParent()).getInvoker());
   return (MunchTab)parent.getParent().getParent().getParent();
  }

 }
}
